/*
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "cert_manager_api.h"
#include "cm_ipc_client.h"
#include "cm_log.h"
#include "cm_mem.h"
#include "cm_type.h"
#include <string.h>
#include <securec.h>

CM_API_EXPORT int32_t CmGetUserCertInfo(const struct CmBlob *certUri, uint32_t store, struct CertInfo *certificateInfo)
{
    CM_LOG_D("enter get cert info");
    if ((certUri == NULL) || (certificateInfo == NULL)) {
        return CMR_ERROR_NULL_POINTER;
    }

    int32_t ret = CmClientGetUserCertInfo(certUri, store, certificateInfo);
    CM_LOG_D("leave get cert info, result = %d", ret);
    return ret;
}

CM_API_EXPORT int32_t CmGetAppCert(const struct CmBlob *keyUri, const uint32_t store, struct Credential *certificate)
{
    CM_LOG_D("enter get app certificate");
    if (keyUri == NULL || certificate == NULL ||
        (store != CM_CREDENTIAL_STORE && store != CM_PRI_CREDENTIAL_STORE && store != CM_SYS_CREDENTIAL_STORE)) {
        return CMR_ERROR_INVALID_ARGUMENT;
    }

    int32_t ret = CmClientGetAppCert(keyUri, store, certificate);
    CM_LOG_D("leave get app certificate, result = %d", ret);
    return ret;
}

CM_API_EXPORT int32_t CmInit(const struct CmBlob *authUri, const struct CmSignatureSpec *spec, struct CmBlob *handle)
{
    CM_LOG_D("enter cert manager init");
    if ((authUri == NULL) || (spec == NULL) || (handle == NULL)) {
        CM_LOG_E("invalid input arguments");
        return CMR_ERROR_INVALID_ARGUMENT;
    }

    int32_t ret = CmClientInit(authUri, spec, handle);
    CM_LOG_D("leave cert manager init, result = %d", ret);
    return ret;
}

CM_API_EXPORT int32_t CmUpdate(const struct CmBlob *handle, const struct CmBlob *inData)
{
    CM_LOG_D("enter cert manager update");
    if ((handle == NULL) || (inData == NULL)) {
        CM_LOG_E("invalid input arguments");
        return CMR_ERROR_INVALID_ARGUMENT;
    }

    int32_t ret = CmClientUpdate(handle, inData);
    CM_LOG_D("leave cert manager update, result = %d", ret);
    return ret;
}

CM_API_EXPORT int32_t CmFinish(const struct CmBlob *handle, const struct CmBlob *inData, struct CmBlob *outData)
{
    CM_LOG_D("enter cert manager finish");
    if ((handle == NULL) || (inData == NULL) || (outData == NULL)) {
        CM_LOG_E("invalid input arguments");
        return CMR_ERROR_INVALID_ARGUMENT;
    }

    int32_t ret = CmClientFinish(handle, inData, outData);
    CM_LOG_D("leave cert manager finish, result = %d", ret);
    return ret;
}

CM_API_EXPORT int32_t CmAbort(const struct CmBlob *handle)
{
    CM_LOG_D("enter cert manager abort");
    if (handle == NULL) {
        CM_LOG_E("invalid input arguments");
        return CMR_ERROR_INVALID_ARGUMENT;
    }

    int32_t ret = CmClientAbort(handle);
    CM_LOG_D("leave cert manager abort, result = %d", ret);
    return ret;
}

static void trim_cert_uri(char *str)
{
    if (str) {
        int len = strlen(str);
        while (len) {
            char end = str[len - 1];
            if (end == '\n' || end == ' ') {
                str[len - 1] = 0;
                len--;
            } else {
                break;
            }
        }
    }
}

CM_API_EXPORT char *load_ca_cert(const char *uri)
{
    if (!uri) {
        CM_LOG_E("load_ca_cert: invalid uri");
        return NULL;
    }
    CM_LOG_I("load_ca_cert: uri=%s", uri);

    uint8_t uri_buf[MAX_LEN_URI];
    memset_s(uri_buf, MAX_LEN_URI, 0, MAX_LEN_URI);
    int uri_len = strlen(uri);
    memcpy_s(uri_buf, MAX_LEN_URI, (uint8_t *)uri, uri_len);
    trim_cert_uri((char *)uri_buf);
    struct CmBlob uri_blob = { MAX_LEN_URI, (uint8_t *)uri_buf };

    struct CertInfo caCert;
    unsigned int len = sizeof(struct CertInfo);
    (void)memset_s(&caCert, len, 0, len);
    caCert.certInfo.data = (uint8_t *)malloc(MAX_LEN_CERTIFICATE);
    if (caCert.certInfo.data == NULL) {
        CM_LOG_E("load_ca_cert: malloc certInfo failed");
        return NULL;
    }
    (void)memset_s(caCert.certInfo.data, MAX_LEN_CERTIFICATE, 0, MAX_LEN_CERTIFICATE);
    caCert.certInfo.size = MAX_LEN_CERTIFICATE;
    int32_t ret = CmGetUserCertInfo(&uri_blob, CM_USER_TRUSTED_STORE, &caCert);
    if (ret != CM_SUCCESS) {
        CM_LOG_E("load_ca_cert: CmGetUserCertInfo failed");
        CM_FREE_BLOB(caCert.certInfo);
        return NULL;
    }
    return (char *)caCert.certInfo.data;
}

CM_API_EXPORT char *load_user_cert(const char *uri)
{
    if (!uri) {
        CM_LOG_E("load_user_cert: invalid uri");
        return NULL;
    }
    CM_LOG_I("load_user_cert: uri=%s", uri);
    uint8_t uri_buf[MAX_LEN_URI];
    memset_s(uri_buf, MAX_LEN_URI, 0, MAX_LEN_URI);
    int uri_len = strlen(uri);
    memcpy_s(uri_buf, MAX_LEN_URI, (uint8_t *)uri, uri_len);
    trim_cert_uri((char *)uri_buf);

    struct CmBlob uri_blob = { MAX_LEN_URI, (uint8_t *)uri_buf };

    struct Credential userCert;
    unsigned int len = sizeof(struct Credential);
    (void)memset_s(&userCert, len, 0, len);
    userCert.credData.data = (uint8_t *)malloc(MAX_LEN_CERTIFICATE_CHAIN);
    if (userCert.credData.data == NULL) {
        CM_LOG_E("load_user_cert: malloc credData failed");
        return NULL;
    }
    (void)memset_s(userCert.credData.data, MAX_LEN_CERTIFICATE_CHAIN, 0, MAX_LEN_CERTIFICATE_CHAIN);
    userCert.credData.size = MAX_LEN_CERTIFICATE_CHAIN;
    int32_t ret = CmGetAppCert(&uri_blob, CM_SYS_CREDENTIAL_STORE, &userCert);
    if (ret != CM_SUCCESS) {
        CM_LOG_E("load_user_cert: CmGetAppCert failed");
        CM_FREE_BLOB(userCert.credData);
        return NULL;
    }
    return (char *)userCert.credData.data;
}

CM_API_EXPORT bool is_cert_uri(const char *str)
{
    if (!str) {
        return false;
    }

    if (!strncmp("oh:t=", str, strlen("oh:t="))) {
        return true;
    }

    return false;
}

CM_API_EXPORT int32_t cm_sign(const char *uri, const unsigned char *dgst, unsigned int dgstlen, unsigned char *sig,
    unsigned int *siglen)
{
    if (!uri || !dgst || !sig) {
        CM_LOG_D("cm_sign invalid params");
        return CM_FAILURE;
    }

    CM_LOG_D("cm_sign start");
    uint8_t uri_buf[MAX_LEN_URI];
    memset_s(uri_buf, MAX_LEN_URI, 0, MAX_LEN_URI);
    int uri_len = strlen(uri);
    memcpy_s(uri_buf, MAX_LEN_URI, (uint8_t *)uri, uri_len);
    trim_cert_uri((char *)uri_buf);

    struct CmBlob authUri = { MAX_LEN_URI, (uint8_t *)uri_buf };

    uint64_t signHandleValue = 0;
    struct CmBlob signHandle = { sizeof(uint64_t), (uint8_t *)&signHandleValue };

    struct CmBlob message = { dgstlen, dgst };

    uint8_t signData[DEFAULT_SIGNATURE_LEN];
    (void)memset_s(signData, DEFAULT_SIGNATURE_LEN, 0, DEFAULT_SIGNATURE_LEN);
    struct CmBlob signature = { DEFAULT_SIGNATURE_LEN, (uint8_t *)signData };

    struct CmSignatureSpec signSpec = { CM_KEY_PURPOSE_SIGN, CM_PADDING_NONE, CM_DIGEST_NONE };
    int32_t ret = CmInit(&authUri, &signSpec, &signHandle);
    if (ret != CM_SUCCESS) {
        CM_LOG_E("cm_sign, CmInit failed, ret = %d ", ret);
        return CM_FAILURE;
    }

    ret = CmUpdate(&signHandle, &message);
    if (ret != CM_SUCCESS) {
        CM_LOG_E("cm_sign, CmUpdate failed, ret = %d ", ret);
        return CM_FAILURE;
    }

    struct CmBlob inData = { 0, NULL };
    ret = CmFinish(&signHandle, &inData, &signature);
    if (ret != CM_SUCCESS) {
        CM_LOG_E("cm_sign, CmFinish failed, ret = %d ", ret);
        return CM_FAILURE;
    }

    *siglen = signature.size;
    memcpy_s(sig, signature.size, signature.data, signature.size);
    return CM_SUCCESS;
}